"""
__project__ = 'PythonProject'
__file_name__ = 'test6'
__author__ = 'Sun'
__time__ = '2021/4/15 11:45'
__product_name = PyCharm

"""


# 有参数的__init__()方法

# class Dog(object):
#
#     def __init__(self):
#         # 设置属性
#         self.name = "旺财"
#         self.color = "白色"
#
#     def info(self):
#         print("名字:%s" % self.name)
#         print("毛色:%s" % self.color)
#
#
#
# # 创建一个对象
# wangcai1 = Dog()
# wangcai1.info()

#
class Dog(object):

    def __init__(self, new_name, new_color, new_age=5):
        # 设置属性
        self.name = new_name
        self.color = new_color
        self.age = new_age

    def info(self):
        print("名字:%s" % self.name)
        print("毛色:%s" % self.color)
        print("年龄:%d" % self.age)


wangcai = Dog("旺财", "白色")
wangcai.info()

#
zangao = Dog("藏獒", "黑色", 6)
zangao.info()

#————————————————————————————————————————————————————————————————————————————————————

class Dog(object):

    def __init__(self, new_name, new_color, new_age=5):
        # 设置属性
        self.name = new_name
        self.color = new_color
        self.age = new_age

    # 在实际开发中不会定义一个方法 来追踪对象属性信息变化
    # def info(self):
    #     print("名字:%s" % self.name)
    #     print("毛色:%s" % self.color)
    #     print("年龄:%d" % self.age)

    # 作用: 是追踪对象属性信息变量 一般用于程序员调试代码
    # 特点: 有且只有一个形参 那就是self
    # 他必须有返回值 返回值类型为字符串
    # 什么时候被python调用: 在当前监听到程序员打印这个类创建的对象的时候
    def __str__(self):
        return "名字:%s 毛色:%s 年龄:%d" % (self.name, self.color, self.age)


# 旺财
wangcai = Dog("旺财", "白色")
# <__main__.Dog object at 0x000000000218EB38>
# 在我们没有实现__str__方法的时候 打印对象 会输出十六进制地址
# 如果我们实现了__str__方法 打印对象 会输出str方法中的字符串
print(wangcai)
# wangcai.info()

# 藏獒
zangao = Dog("藏獒", "黑色", 8)
# zangao.info()
print(zangao)

#————————————————————————————————————————————————————————————————

# # 自定义一个英雄类
# class Hero(object):
#
#     def __init__(self, name, age):
#         self.name = name
#         self.age = age
#
#     def __str__(self):
#         return "名字:%s 年龄:%d" % (self.name, self.age)
#
#     # 监听对象销毁的
#     def __del__(self):
#         print("再见!!!")
#
# huangzhong = Hero("黄忠", 500)
# print(huangzhong)
# # 当程序的代码执行完成后 将要结束 那么会执行del huanghzong 杀死对象 系统释放内存


#——————————————————————————————————————————————————————————
#（单继承）

# 故事情节：煎饼果子老师傅在煎饼果子界摸爬滚打几十年，拥有一身精湛的煎饼果子技术，
# 并总结了一套"古法煎饼果子配方"。

class Master(object):

    def __init__(self):
        # 属性
        self.kongfu = "古法配发"

    # 会制作煎饼果子
    def make_cake(self):
        print("会制作古法煎饼果子")


# 自定义一个李师傅
lishifu = Master()
print(lishifu.kongfu)
lishifu.make_cake()

print("-" * 30)


# 在python类中 子类继承了父类 子类就拥有了父类的属性和方法
# 格式: class 子类名(父类名):
# 单继承 就是只有一个父类
class Prentice(Master):
    pass


damao = Prentice()
print(damao.kongfu)
damao.make_cake()



# ——————————————————————————————————————————————————————————————————

# 李师傅类
class Master(object):

    def __init__(self):
        print(self)
        self.kongfu = "古法配方"

    def make_cake(self):
        print("古法煎饼果子")

    # 大烟袋
    def dayandai(self):
        print("大烟袋")


# 新东方
class School(object):

    def __init__(self):
        self.kongfu = "现代配方"

    def make_cake(self):
        print("现代煎饼果子")

    def xiaoyandai(self):
        print("小烟袋")


# 大猫 继承了李师傅 继承新东方
# 多继承格式: class 子类名(父类1, 父类2, ...):
class Prentice(Master, School):
    pass


# 创建一个大猫
damao = Prentice()
print(damao)
print(damao.kongfu)
damao.make_cake()
damao.dayandai()
damao.xiaoyandai()
"""
# 如果子类继承了多个父类 如果父类的方法名相同 那么子类会继承第一个父类方法
# 如果子类继承了多个父类 如果父类的方法名不同 那么子类会全部继承方法

# 在使用类创建一个对象的时候就会执行init方法
# 如果子类实现了init方法 会走自己子类中的init方法
# 如果子类没有实现init方法 会找继承父类的init方法

# 为什么子类继承了父类 子类就拥有了父类的属性?
# 前提是继承了父类的init方法 才使用父类的init 给自己设置属性
# 先继承方法的 然后继承了父类的属性
"""
# ————————————————————————————————————————————————————————————

# 古法师傅类
class Master(object):

    def __init__(self):
        self.kongfu = "古法配方"

    def make_cake(self):
        print("古法煎饼果子")


# 现代师傅类
class School(object):
    def __init__(self):
        self.kongfu = "现代配方"

    def make_cake(self):
        print("现代煎饼果子")


# 徒弟类多继承
# 大猫 猫氏配方
# 会做 猫氏煎饼果子
class Prentice(Master, School):

    # 当前类中的无论init方法 还是 make_cake 方法都叫做重写
    # 重写: 子类继承了父类 在子类中实现了父类已有的方法或者属性 就叫做重写
    # 子类继承父类 重写父类的方法 做自己特有的事情
    # 特有事情: init 设置kongfu的属性值为猫氏配方
    # make_cake 打印 猫氏煎饼果子
    def __init__(self):
        self.kongfu = "猫氏配方"

    def make_cake(self):
        print("猫氏煎饼果子")

    # 自定义方法
    def hello_python(self):
        print("你好python")


damao = Prentice()
print(damao.kongfu)
damao.make_cake()
damao.hello_python()
"""
子类继承了父类 
子类重写了父类的同名的属性和方法
那么子类会使用自己的属性 和 方法
"""
# -----------------------------------------------------------


# 古法师傅类
class Master(object):

    def __init__(self):
        self.kongfu = "古法配方"

    def make_cake(self):
        print("古法煎饼果子")


# 现代师傅类
class School(object):
    def __init__(self):
        self.kongfu = "现代配方"

    def make_cake(self):
        print("现代煎饼果子")


# 徒弟类多继承
# 大猫 猫氏配方
# 会做 猫氏煎饼果子
class Prentice(Master, School):

    def __init__(self):
        self.kongfu = "猫氏配方"
        School.__init__(self)

    # def set_kongfu(self):
    #     self.kongfu = "古法配方"

    # 猫氏煎饼果子
    def make_cake(self):
        print("猫氏煎饼果子")

    # 古法
    def make_old_cake(self):
        print("制作古法煎饼果子-大猫")
        # 格式: 父类名.同名方法名(self)
        Master.make_cake(self)

    # 现代
    def make_new_cake(self):
        print("制作现代煎饼果子-大猫")
        School.make_cake(self)


damao = Prentice()
print(damao.kongfu)
damao.make_cake()
damao.make_old_cake()
damao.make_new_cake()

# ————————————————————————————————————————————————————————————————

# 古法师傅类
class Master(object):

    def __init__(self):
        self.kongfu = "古法配方"

    def make_cake(self):
        print("古法煎饼果子")


# 现代师傅类
class School(object):
    def __init__(self):
        self.kongfu = "现代配方"

    def make_cake(self):
        print("现代煎饼果子")


# 徒弟类多继承
# 大猫 猫氏配方
# 会做 猫氏煎饼果子
class Prentice(Master, School):

    def __init__(self):
        self.kongfu = "猫氏配方"

    # 猫氏煎饼果子
    def make_cake(self):
        print("猫氏煎饼果子")

    # 古法
    def make_old_cake(self):
        # 子类调用父类的同名方法 一共有三种方法:

        # 01:
        # 格式: 父类名.同名方法名(self)
        # Master.make_cake(self)

        # 02:
        # 格式: super(子类名, self).同名方法名()
        # super(Prentice, self).make_cake()

        # 03:
        # 格式: super().同名方法名()
        # 03格式是02格式的简写

        # 02 和 03 只适用于新式类
        super().make_cake()

    # 现代
    def make_new_cake(self):
        # 01:
        # School.make_cake(self)
        # 02:
        # super(Prentice, self).make_cake()
        # 03:
        super().make_cake()


damao = Prentice()
# print(damao.kongfu)
# damao.make_cake()
damao.make_old_cake()
damao.make_new_cake()

"""
如果子类继承了父类 子类重写了父类的同名方法 而且子类想调用父类的这个同名方法一共有三种方式
# 01 父类名.父类方法名(self)
# 02 super(子类名, self).父类方法名()
# 03 super().父类方法名()

# 02 和 03 只适用于新式类
# 02 和 03 一般用于单继承  也可以是多继承 但是只能调用第一个父类的方法
# 01 适用于单继承或者多继承都可以
"""
# ————————————————————————————————————————————————————————————————————————————

# # 古法
# class Master(object):
#
#     def __init__(self):
#         # 配方
#         self.kongfu = "古法配方"
#         # 钱
#         # 如果一个属性是以两个下划线开始 就标识这个这个属性是一个私有属性
#         self.__money = 1000000
#
#
#     def make_cake(self):
#         print("古法煎饼果子")
#
#     # 如果一个方法是以两个下划线开始 也代表已经私有
#     def __hello_python(self):
#         print("你好python")
#
# # 徒弟类
# class Prentice(Master):
#     pass
#
#
# damao = Prentice()
# print(damao.kongfu)
# # 子类继承了父类 如果父类的属性私有 将不会被子类继承
# # print(damao.money)
# damao.make_cake()
# # 子类继承了父类 如果父类的属性私有 将不会被子类继承
# damao.hello_python()


# 古法
class Master(object):

    def __init__(self):
        # 配方
        self.kongfu = "古法配方"
        # 钱
        # 如果一个属性是以两个下划线开始 就标
        self.__money = 10000

    def make_cake(self):
        print("古法煎饼果子")
        # 私有属性和私有方法可以在类的里面使用
        print(self.__money)
        self.__hello_python()

    # 如果一个方法是以两个下划线开始 也代表已经私
    def __hello_python(self):
        print("你好python")


lishifu = Master()
# print(lishifu.kongfu)
# 自定义类中 如果一个属性进行了私有 在类的外面不能调用
# print(lishifu.money)
# 自定义类中 如果一个方法进行了私有 在类的外面不能调用
# lishifu.hello_python()


# 测试
lishifu.make_cake()


# ————————————————————————————————————————————————————————————————————

class Person(object):

    def __init__(self):
        self.name = "小明"
        self.__age = 20

    # 获取私有属性的值
    def get_age(self):
        return self.__age

    # 设置私有属性的值
    def set_age(self, new_age):
        self.__age = new_age


# 定义一个对象
p = Person()
# 强行获取私有属性
# 崇尚一切靠自觉
print(p._Person__age)
print(p.name)
# 想在类的外面获取对象的属性
# ret = p.get_age()
# print(ret)

# 想在类的外面修改对象私有属性的值
p.set_age(30)
print(p.get_age())
# ——————————————————————————————————————————————————————————

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print("吃饭")


# 小明
xm = Person("小明", 22)
# print(id(xm.name))
print(id(xm.eat()))
xh = Person("小红", 33)
# print(id(xh.name))
print(id(xh.eat()))
print(id(Person))
# 通过地址可以看到  通过相同的类创建的对象不同

# 小明和小红的name id不同
# 因为需要保存各自的属性值
# 不同的对象调用相同的方法 方法id相同
# 因为实例方法中有一个self 可以知道是哪个对象调用的 没有必要开辟内存
# ——————————————————————————————————————————————————————

# # 自定义一个类
# class Person(object):
#
#     # 类属性
#     # 类属性定义在类的里面 而且在方法的外面 就称之为类属性
#     country = "中国"
#
#     def __init__(self):
#         # 实例属性 对象属性
#         self.name = "小明"

# 类属性
# 取值
# 01: 类名.类属性名
# print(Person.country)
# 02: 对象名.类属性名
# p = Person()
# print(p.country)

# 赋值 只有一种方式
# 01: 类名.类属性名 = 值
# Person.country = "荷兰"
# print(Person.country)
# 02: 对象名.类属性名 其实是 对象名.实例属性名(这种方式不存在)
# p = Person()
# 是给p这个对象添加了一个实例属性(只是和类属性名相同而已)
# p.country = "河南"
# print(Person.country)
# 打印实例属性
# print(p.country)
# 但是我们就想使用p.country 打印 中国
# 删除实例属性
# del p.country
# print(p.country)


# 不同的对象调用这个类属性 这个类属性的值相同me id相同me
# id相同啊 值相同
# 类属性值有一个 只会开辟一份内存
# 他是类的属性 类的特征
# p1 = Person()
# print(id(p1.country))
#
# p2 = Person()
# print(id(p2.country))
#
# p3 = Person()
# print(id(p3.country))
#
# print(id(Person.country))


# 实例属性(取值和赋值)
# 取值
# p = Person()
# # print(p.name)
# # 赋值
# p.name = "小小明"
# print(p.name)


# 类属性的作用
# 自定义一个类
# 对象属性
# class Person(object):
#
#     def __init__(self, name, country="中国"):
#         # 实例属性 对象属性
#         self.name = name
#         self.country = country
#
#
# p1 = Person("小明")

class Person(object):
    country = "中国"

    def __init__(self, name):
        # 实例属性 对象属性
        self.name = name

# p1 = Person("小明")
# print(p1.country)


# 自定义一个类
class Person(object):
    # 类属性
    __country = "中国"

    def __init__(self):
        # 实例属性
        self.__name = "小明"

    # 实例方法
    def set_name(self, new_name):
        self.__name = new_name

    def get_name(self):
        return self.__name

    # 类方法
    # 对类的私有属性进行取值
    @classmethod
    def set_country(cls, new_country):
        cls.__country = new_country

    @classmethod
    def get_country(cls):
        return cls.__country

    # 静态方法
    # 没有默认的形参我们就把他称为静态方法
    @staticmethod
    def hello_python():
        print("你好python")


# 格式:def 方法名(self):
# 01 实例方法 调用
# 01格式: 对象名.实例方法名()
# p = Person()
# print(p.get_name())

# 格式:@classmethod
#      def 方法名(cls)
# 02 类方法 调用
# 01格式: 类名.类方法名()
# print(Person.get_country())
# 02格式: 对象名.类方法名()
# p = Person()
# print(p.get_country())

# 格式:@staticmethod
#      def 方法名():
# 03 静态方法 调用
# 01格式: 类名.静态方法名()
# Person.hello_python()
# 02格式: 对象名.静态方法名()
# p = Person()
# p.hello_python()


# 面向对象的三大特征:
#
# 1. 封装：属性和方法放到类内部，通过对象访问属性或者方法,
#
#
# 2. 继承：子类需要复用父类里面的属性或者方法,
# 		当然子类还可以提供自己的属性和方法;
#
# 3. 多态：同一个方法不同对象调用同一个方法功能的表现形式不一样，
# 例如:1.不同的两个对象，字符串的加法和整数的加法，
# 		同样是加法，实现的功能是不一样的；
# 	2.这两个对象之间没有任何直接继承关系，
# 		但是所有对象的最终父类都是元类；

class Animal(object):
    def run(self):
        print('Animal is running...')


class Dog(Animal):

    def run(self):
        print('Dog is running...')

    def eat(self):
        print('Eating meat...')


class Cat(Animal):

    def run(self):
        print('Cat is running...')


# 当子类和父类都存在相同的run()方法时，我们说，
# 子类的run()覆盖了父类的run()，在代码运行的时候，
# 总是会调用子类的run()。
# 这样，我们就获得了继承的另一个好处：多态。
#
# 要理解什么是多态，我们首先要对数据类型再作一点说明。当我们定义一个class的时候，
# 我们实际上就定义了一种数据类型。
# 我们定义的数据类型和Python自带的数据类型，比如str、list、dict没什么两样：

a = list()  # a是list类型
b = Animal()  # b是Animal类型
c = Dog()  # c是Dog类型

#判断一个变量是否是某个类型可以用isinstance()
#判断：

# >> > isinstance(a, list)
# True
# >> > isinstance(b, Animal)
# True
# >> > isinstance(c, Dog)
# True

# 看来a、b、c确实对应着list、Animal、Dog这3种类型。
#
# 但是等等，试试：

# >> > isinstance(c, Animal)
True

# 看来c不仅仅是Dog，c还是Animal！
#
# 不过仔细想想，这是有道理的，因为Dog是从Animal继承下来的，
# 当我们创建了一个Dog的实例c时，
# 我们认为c的数据类型是Dog没错，
# 但c同时也是Animal也没错，
# Dog本来就是Animal的一种！
#
# 这就是多态，同样的东西可以可以是不同类型

# 所以，在继承关系中，如果一个实例的数据类型是某个子类，
# 那它的数据类型也可以被看做是父类。但是，反过来就不行：

# >> > b = Animal()
# >> > isinstance(b, Dog)
# False


# Dog可以看成Animal，但Animal不可以看成Dog。
#
# 要理解多态的好处，我们还需要再编写一个函数，
# 这个函数接受一个Animal类型的变量：

def run_twice(animal):
    animal.run()
    animal.run()

# 当我们传入Animal的实例时，run_twice()就打印出：

# >> > run_twice(Animal())
# Animal is running...
# Animal is running...
#
# 当我们传入Dog的实例时，run_twice()
# 就打印出：
#
# >> > run_twice(Dog())
# Dog is running...
# Dog is running...
#
# 当我们传入Cat的实例时，run_twice()
# 就打印出：
#
# >> > run_twice(Cat())
# Cat is running...
# Cat is running...
#
# 看上去没啥意思，但是仔细想想，现在，如果我们再定义一个Tortoise类型，
# 也从Animal派生：
#
# class Tortoise(Animal):
#     def run(self):
#         print('Tortoise is running slowly...')
#
#
# 当我们调用run_twice()
# 时，传入Tortoise的实例：
#
# >> > run_twice(Tortoise())
# Tortoise is running
# slowly...
# Tortoise is running
# slowly...
#
# 你会发现，新增一个Animal的子类，不必对run_twice()
# 做任何修改，
# 实际上，任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行，
# 原因就在于多态。
#
# 多态的好处就是，当我们需要传入Dog、Cat、Tortoise……时，
# 我们只需要接收Animal类型就可以了，
# 因为Dog、Cat、Tortoise……都是Animal类型，然后，按照Animal类型进行操作即可。
# 由于Animal类型有run()
# 方法，
# 因此，传入的任意类型，只要是Animal类或者子类
# ，就会自动调用实际类型的run()
# 方法，这就是多态的意思：
#
# 对于一个变量，我们只需要知道它是Animal类型，无需确切地知道它的子类型，就可以放心地调用run()
# 方法，而具体调用的run()
# 方法是作用在Animal、Dog、Cat还是Tortoise对象上，由运行时该对象的确切类型决定，这就是多态真正的威力：调用方只管调用，不管细节，而当我们新增一种Animal的子类时，只要确保run()
# 方法编写正确，不用管原来的代码是如何调用的。这就是著名的“开闭”原则：
#
# 对扩展开放：允许新增Animal子类；
#
# 对修改封闭：不需要修改依赖Animal类型的run_twice()
# 等函数。
#
# 继承还可以一级一级地继承下来，就好比从爷爷到爸爸、再到儿子这样的关系。而任何类，最终都可以追溯到根类object，这些继承关系看上去就像一颗倒着的树。比如如下的继承树：
#
# ┌───────────────┐
# │    object     │
# └───────────────┘
# │
# ┌────────────┴────────────┐
# │                         │
# ▼                         ▼
# ┌─────────────┐           ┌─────────────┐
# │   Animal    │           │    Plant    │
# └─────────────┘           └─────────────┘
# │                         │
# ┌─────┴──────┐            ┌─────┴──────┐
# │            │            │            │
# ▼            ▼            ▼            ▼
# ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
# │   Dog   │  │   Cat   │  │  Tree   │  │ Flower  │
# └─────────┘  └─────────┘  └─────────┘  └─────────┘
#
#
#
#
#
# 直面面向对象思想的本质，
# 其实不管是封装，继承还是多态，
# 它们的本质都是为了让程序员快速开发，
# 提高代码的复用性，


